home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 January / macformat-020.iso / Shareware City / Developers / apps.to.go / AppsToGo.docs / =AppWannabe.mult.doc.app < prev    next >
Encoding:
Text File  |  1993-04-12  |  9.7 KB  |  232 lines  |  [TEXT/MPS ]

  1. This document assumes that you have read or are familiar with the contents of the
  2. documents “AppWannabe.Tutorial” and “AppWannabe.multiple.doc.app”.  This document
  3. addresses the needs of an application that has more than one document type.
  4.  
  5. The sample application DTS.Draw demonstrates a good technique for handling
  6. multiple documents with the DTS application framework.
  7.  
  8. The assumption here is that you have already implemented the first document type to
  9. some degree.  If this is so and you have followed the method described by the
  10. document “AppWannabe.multiple.doc.app”, then you have already placed some code in the
  11. file “Window2.c”.
  12.  
  13. The application framework was designed to make writing an application with a single
  14. document type.  All that needs to be done for this is to add code to various empty
  15. or nearly empty functions within AppWannabe.  Supporting more document types demands a
  16. different technique.
  17.  
  18. You can consider the “Window2.c” file as the first document object.  Almost all of the
  19. code for handling this document is contained in this single file.  There is a little
  20. bit of setup code in the file “File2.c”, but for the most part the code for this initial
  21. document is in the file “Window2.c”.
  22.  
  23. What you need for your second document is another “Window2.c” file.  DTS.Draw demonstrates
  24. this.  Here are two other document code files for DTS.Draw:
  25.     Clipboard.c
  26.     ToolPalette.c
  27.  
  28. These files are quite similer in their content.  They both have a document initialization
  29. function.  In looking at this function, you can easily see how the document will differ
  30. from the primary document (whose code is contained in “Window2.c”).
  31.  
  32. For the “Clipboard.c” file, here's the initialization function:
  33.  
  34. OSErr    ClipboardInitDocument(FileRecHndl frHndl)
  35. {
  36.     OSErr        err;
  37.     FileRecPtr    frPtr;
  38.  
  39.     err = DefaultInitDocument(frHndl, 0, 0, 0);
  40.  
  41.     if (!err) {
  42.         frPtr = *frHndl;
  43.         frPtr->fileState.windowID                = rClipboardWindow;
  44.         frPtr->fileState.calcFrameRgnProc        = nil;
  45.         frPtr->fileState.contentClickProc        = ClipboardContentClick;
  46.         frPtr->fileState.contentKeyProc          = ClipboardContentKey;
  47.         frPtr->fileState.drawFrameProc           = nil;
  48.         frPtr->fileState.freeDocumentProc        = nil;
  49.         frPtr->fileState.freeWindowProc          = nil;
  50.         frPtr->fileState.initContentProc         = ClipboardInitContent;
  51.         frPtr->fileState.readDocumentProc        = nil;
  52.         frPtr->fileState.readDocumentHeaderProc  = nil;
  53.         frPtr->fileState.resizeContentProc       = nil;
  54.         frPtr->fileState.scrollFrameProc         = nil;
  55.         frPtr->fileState.undoFixupProc           = nil;
  56.         frPtr->fileState.windowCursorProc        = ClipboardWindowCursor;
  57.         frPtr->fileState.writeDocumentProc       = nil;
  58.         frPtr->fileState.writeDocumentHeaderProc = nil;
  59.  
  60.         frPtr->fileState.fss.name[0] = 0;        /* Use resource window name. */
  61.         frPtr->fileState.attributes  = kwClipboardWindow;
  62.     }
  63.  
  64.     return(err);
  65. }
  66.  
  67. Many of the document functions aren't needed for this document.  The ones that aren't
  68. needed have the procedure pointer set to nil.  If the procPtr is nil, then the
  69. application framework knows that the functionality for that function isn't needed for
  70. this document.  For example:  For the clipboard function, there is no CalcFrameRgn
  71. function, so the procPtr is simply set nil.
  72.  
  73. Note that content clicks are handled differently than the primary document.  Content
  74. clicks either scroll the clipboard or do nothing.  The code for this is very simple:
  75.  
  76. static void    ClipboardContentClick(WindowPtr window, EventRecord *event, Boolean firstClick)
  77. {
  78. #pragma unused (frHndl, firstClick)
  79.  
  80.     IsCtlEvent(window, event, nil, nil);
  81. }
  82.  
  83.  
  84. IsCtlEvent handles document scrolling.  Nothing else is needed. The ContentKey function is
  85. even simpler.  Keypresses do nothing.  All we need to do is to state that the window used
  86. the keypress is used.  Here's the function:
  87.  
  88. static Boolean    ClipboardContentKey(WindowPtr window, EventRecord *event, Boolean *passThrough)
  89. {
  90. #pragma unused (window, event, passThrough)
  91.  
  92.     return(true);
  93. }
  94.  
  95.  
  96.  
  97. This is how the behavior for the various documents is changed from the primary document.
  98. The procPtr is simply changed to point to a different function.
  99.  
  100.  
  101. Note that the ImageDocument procedure isn't changed or set to nil.  That is because we
  102. want the ImageDocument function in the primary document to be called.  Unless we change
  103. the procPtr, the primary document's function will be called.  This is exactly what we
  104. want, as the clipboard is simply a non-editable standard document.  All of the procPtrs
  105. that would support editing (or saving) the standard document have been changed.
  106.  
  107. Note that we didn't change or nil out the functions for reading a document.  They still
  108. point to the primary document's read code.  This is fine because there will never be an
  109. instance where the clipboard can be opened and read.  We don't have to worry about
  110. changing these procPtrs because it simply can't happen that they are called.
  111.  
  112.  
  113. The ClipboardInitDocument function is called from “File2.c”.  Instead of having this
  114. initialization code within “File2.c” it is placed here and called.  This allows a
  115. better grouping of the clipboard code.  The more of it that is in one place, the
  116. easier it is to follow what the clipboard document does.
  117.  
  118.  
  119.  
  120. Here's the initialization code for the tool palette document.  You can see very quickly
  121. that it does practically nothing.  It only handles clicks and drawing itself.
  122.  
  123. OSErr    ToolInitDocument(FileRecHndl frHndl)
  124. {
  125.     FileRecPtr    frPtr;
  126.  
  127.     frPtr = *frHndl;
  128.     frPtr->fileState.windowID                = rToolWindow;
  129.     frPtr->fileState.getDocWindow            = GetToolWindow;
  130.     frPtr->fileState.calcFrameRgnProc        = nil;
  131.     frPtr->fileState.contentClickProc        = ToolContentClick;
  132.     frPtr->fileState.contentKeyProc          = nil;
  133.     frPtr->fileState.drawFrameProc           = nil;
  134.     frPtr->fileState.freeDocumentProc        = nil;
  135.     frPtr->fileState.freeWindowProc          = nil;
  136.     frPtr->fileState.imageProc               = ToolImageDocument;
  137.     frPtr->fileState.initContentProc         = nil;
  138.     frPtr->fileState.readDocumentProc        = nil;
  139.     frPtr->fileState.readDocumentHeaderProc  = nil;
  140.     frPtr->fileState.resizeContentProc       = nil;
  141.     frPtr->fileState.scrollFrameProc         = nil;
  142.     frPtr->fileState.undoFixupProc           = nil;
  143.     frPtr->fileState.windowCursorProc        = nil;
  144.     frPtr->fileState.writeDocumentProc       = nil;
  145.     frPtr->fileState.writeDocumentHeaderProc = nil;
  146.  
  147.     frPtr->fileState.fss.name[0] = 0;    /* Use resource window name. */
  148.     frPtr->fileState.attributes  = kwToolWindow;
  149.  
  150.     return(noErr);
  151. }
  152.  
  153.  
  154. The GetToolWindow code places the initial window in the upper-right of the main screen.
  155. The constant kwToolWindow includes a bit that states that the window is a floating palette.
  156. That's all there is to creating this floating tool palette.
  157.  
  158. There is some additional code for moving the window if the user tears off the tool menu.
  159. Again, it is logical to place this code here along with all of the other code for this
  160. document.
  161.  
  162. Note that the concept of a “document” for this window is only a convenience.  It can never
  163. be saved or opened or printed.  Considering it a document allows us to use the framework
  164. to manage the floating palette.
  165.  
  166. That's about all there is to using the framework for multiple documents.  Currently all of
  167. the other multiple document issues will have to be handled by you.  One such issue is managing
  168. menus.  The framework has no support for this.  You will have to find out the document type
  169. of the top window and adjust the menus accordingly.
  170.  
  171. Another such issue is that some of these documents may be related.  If you close a document,
  172. you may also need to close other documents that were created in relation to that document.
  173. Again, you will have to handle these issues yourself.
  174.  
  175. There are interator functions in the framework that conveniently allow you to find the next
  176. document of a certain type.  These iterators walk the window list looking for the next
  177. window that has a document of a certain type.  This means that you can use these iterators
  178. only for documents that have a window.
  179.  
  180.  
  181.  
  182. If you are a heavy user of the hierarchcal document architecture, you may end up with two
  183. different root object definitions for two different document types.  It is convenient to
  184. store some non-undoable state data in the root object, but if two different documents have
  185. different state data, having only one root object type can get confusing.  Due to this, you
  186. may find it useful to sub-type the root object in these instances.
  187.  
  188. The standard root object definitions looks like this:
  189.  
  190. long    TRootObj(TreeObjHndl hndl, short message, long data);
  191. typedef struct {
  192.     TreeObjHndl    undo;        /* This structure may be added to, but */
  193.     FileRecHndl    frHndl;        /* these two first fields must remain. */
  194. } RootObj;
  195.  
  196. You may wish to extend this to be something like the following:
  197.  
  198. long    TRootObj(TreeObjHndl hndl, short message, long data);
  199. typedef struct {
  200.     TreeObjHndl    undo;        /* This structure may be added to, but */
  201.     FileRecHndl    frHndl;        /* these two first fields must remain. */
  202.     short        subtype;    /* Identifier for variant of root object. */
  203.     union {
  204.         Root1    r1;            /* Union in the various root definitions here. */
  205.         Root2    r2;
  206.     } st;
  207. } RootObj;
  208.  
  209.  
  210. The code for the root object that is called by the framework would look something like:
  211.  
  212. long    TRootObj(TreeObjHndl hndl, short message, long data)
  213. {
  214.     TreeObjProcPtr    proc;
  215.  
  216.     if (proc = gRootMethods[mDerefRoot(hndl)->subtype])
  217.         return((*proc)(hndl, message, data);
  218.  
  219.     return(0);
  220. }
  221.  
  222. This code is dispatched to for a root object of whatever subtype.  It in turn then dispatches
  223. to the code for the subtype of the root object.  A table of procedures called gRootMethods is
  224. assumed here.  It would be constructed similarly to the gTreeObjMethods table found in the
  225. file “File.c”.
  226.  
  227.  
  228. The rest is simply a matter of writing code.  You will probably want to reference the various
  229. samples that use the framework to see how certain issues were addressed.
  230.  
  231.  
  232.